#include "LogFormatter.h"
#include "Message.h"
#include <sstream>

namespace mylog
{
    class LogLevelFormatItem : public LogFormatter::FormatItem
    {
    public: 
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << LogLevel::ToString(message->level_);
        }
    };

    class FileNameFormatItem : public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << message->filename_;
        }
    };

    class LineFormatItem : public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << message->line_;
        }
    };

    class LoggerNameFormatItem: public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << message->logger_name_;
        }
    };

    class MessageFormatItem : public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << message->payload_;
        }
    };

    class ThreadIdFormatItem : public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << message->tid_;
        }
    };

    class TimeFormatItem : public LogFormatter::FormatItem
    {
    public:
        TimeFormatItem(std::string_view time_pattern = "%Y-%m-%d %H:%M:%S")        
            : time_pattern_(time_pattern)
        {
        }
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            struct tm t;
            localtime_r(&message->ctime_, &t);
            char buf[128];
            strftime(buf, sizeof(buf), time_pattern_.c_str(), &t);
            os << buf;
        }
    private:
        std::string time_pattern_;
    };

    class TabFormatItem : public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << '\t';
        }
    };

    class LineBreakFormatItem : public LogFormatter::FormatItem
    {
    public:
        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << '\n';
        }
    };

    class StringFormatItem : public LogFormatter::FormatItem
    {
    public: 
        StringFormatItem(std::string_view str)
            : str_(str)
        {
        }

        void format(std::ostream& os, std::shared_ptr<LogMessage> message) override
        {
            os << str_;
        }
    private:
        std::string str_;
    };

    LogFormatter::LogFormatter(std::string_view pattern)
    {
        if (pattern.empty())
        {
            pattern_ = DEFAULT_PATTERN;
        }
        else 
        {
            pattern_ = pattern;
        }
        try 
        {
            initFormatItems();
        }
        catch (const std::exception& e)
        {
            std::cerr << e.what() << std::endl;
            pattern_ = DEFAULT_PATTERN;
            initFormatItems();
        }
    }

    std::string LogFormatter::format(std::shared_ptr<LogMessage> message)
    {
        std::stringstream ss;
        for (auto& item : format_items_)
        {
            item->format(ss, message);
        }
        return ss.str();
    }

    void LogFormatter::initFormatItems()
    {
        int idx = 0;
        while(idx < pattern_.size())
        {
            if (pattern_[idx] == '%')
            {
                idx++;
                if (pattern_[idx] == 'P')
                    format_items_.emplace_back(std::make_shared<LogLevelFormatItem>());
                else if (pattern_[idx] == 'F')
                    format_items_.emplace_back(std::make_shared<FileNameFormatItem>());
                else if (pattern_[idx] == 'L')
                    format_items_.emplace_back(std::make_shared<LineFormatItem>());
                else if (pattern_[idx] == 'C')
                    format_items_.emplace_back(std::make_shared<LoggerNameFormatItem>());
                else if (pattern_[idx] == 'M')
                    format_items_.emplace_back(std::make_shared<MessageFormatItem>());
                else if (pattern_[idx] == 'T')
                    format_items_.emplace_back(std::make_shared<ThreadIdFormatItem>());
                else if (pattern_[idx] == 'D')
                {
                    idx++;
                    if (idx == pattern_.size())
                        break;
                        
                    if (pattern_[idx] != '{') {
                        format_items_.emplace_back(std::make_shared<TimeFormatItem>());
                        idx++;
                        continue;
                    }

                    int begin = ++idx;
                    while (idx < pattern_.size() && pattern_[idx] != '}')
                        idx++;
                    // 开启了自定义时间格式，却没有正确地去设置时间
                    if (pattern_[idx] != '}')
                    {
                        throw std::runtime_error("时间格式设置错误");
                        return;
                    }
                    format_items_.emplace_back(std::make_shared<TimeFormatItem>(pattern_.substr(begin, idx - begin)));
                }
                else if (pattern_[idx] == 't')
                    format_items_.emplace_back(std::make_shared<TabFormatItem>());
                else if (pattern_[idx] == 'n')
                    format_items_.emplace_back(std::make_shared<LineBreakFormatItem>());
                else {
                    throw std::runtime_error("Unknown Identifier");
                }
                idx++;
            }
            else 
            {
                int begin = idx;
                while (idx < pattern_.size() && pattern_[idx] != '%')
                    idx++;
                // 此时idx要么指向%要么就在pattern_的末尾了
                format_items_.emplace_back(std::make_shared<StringFormatItem>(pattern_.substr(begin, idx - begin)));
            }
        }
    }


} // mylog